﻿using Encog;
using Encog.Engine.Network.Activation;
using Encog.ML.Data;
using Encog.ML.Data.Basic;
using Encog.ML.Train;
using Encog.Neural.Networks;
using Encog.Neural.Networks.Layers;
using Encog.Neural.Networks.Training.Propagation.Resilient;
using System;

namespace Bitswap
{
    class Program
    {
        /// <summary>
        /// input for the bitswap function
        /// </summary>
        public static double[][] BitswapInput =
        {
            new[] { 0d, 0d },
            new[] { 0d, 1d },
            new[] { 1d, 0d },
            new[] { 1d, 1d }
        };

        /// <summary>
        /// ideal output for the bitswap function
        /// </summary>
        public static double[][] BitswapIdeal =
        {
            new[] { 0d, 0d },
            new[] { 1d, 0d },
            new[] { 0d, 1d },
            new[] { 1d, 1d }
        };

        static void Main(string[] args)
        {
            // create nework
            var network = new BasicNetwork();
            // input layer            network.AddLayer(new BasicLayer(null, true, 2));
            // hidden layer
            network.AddLayer(new BasicLayer(new ActivationSigmoid(), true, 3));
            // output layer
            network.AddLayer(new BasicLayer(new ActivationSigmoid(), false, 2));
            network.Structure.FinalizeStructure();
            network.Reset();

            // create training data
            IMLDataSet trainingData = new BasicMLDataSet(BitswapInput, BitswapIdeal);

            // train the network
            IMLTrain train = new ResilientPropagation(network, trainingData);
            int epoch = 1;

            do
            {
                train.Iteration();
                Console.WriteLine($@"epoch #{epoch} error: {train.Error}");
                epoch++;
            } while (train.Error > 0.01);
            train.FinishTraining();

            // test the network
            Console.WriteLine(@"neural network results:");
            foreach (var pair in trainingData)
            {
                IMLData output = network.Compute(pair.Input);
                Console.WriteLine($@"input: [{pair.Input[0]}, {pair.Input[1]}], computation result: [{Math.Round(output[0], 2)}; {Math.Round(output[1], 2)}], ideal: {pair.Ideal}");
            }
            EncogFramework.Instance.Shutdown();
            Console.ReadKey();
        }
    }
}
